/*
 * ================================
 * eli960@qq.com
 * http:/linuxmail.cn/
 * 2015-10-20
 * ================================
 */

#include "zcc/zcc_errno.h"

#ifdef _WIN64
#include <WinSock2.h>
#else // _WIN64
#include <errno.h>
#endif // _WIN64

zcc_namespace_begin;

static std::map<int, int> errno_map_sys_to_zcc;
static std::map<int, int> errno_map_zcc_to_sys;

struct code_to_errno_map
{
    int from;
    int to;
};

#ifdef _WIN64
static const struct code_to_errno_map errno_pair[] = {
    {ERROR_INVALID_FUNCTION, ZCC_EINVAL},
    {ERROR_FILE_NOT_FOUND, ZCC_ENOENT},
    {ERROR_PATH_NOT_FOUND, ZCC_ENOENT},
    {ERROR_TOO_MANY_OPEN_FILES, ZCC_EMFILE},
    {ERROR_ACCESS_DENIED, ZCC_EACCES},
    {ERROR_INVALID_HANDLE, ZCC_EBADF},
    {ERROR_ARENA_TRASHED, ZCC_ENOMEM},
    {ERROR_NOT_ENOUGH_MEMORY, ZCC_ENOMEM},
    {ERROR_INVALID_BLOCK, ZCC_ENOMEM},
    {ERROR_BAD_ENVIRONMENT, ZCC_E2BIG},
    {ERROR_BAD_FORMAT, ZCC_ENOEXEC},
    {ERROR_INVALID_ACCESS, ZCC_EINVAL},
    {ERROR_INVALID_DATA, ZCC_EINVAL},
    {ERROR_OUTOFMEMORY, ZCC_ENOMEM},
    {ERROR_INVALID_DRIVE, ZCC_ENOENT},
    {ERROR_NOT_SAME_DEVICE, ZCC_EXDEV},
    {ERROR_NO_MORE_FILES, ZCC_ENOENT},
    {ERROR_WRITE_PROTECT, ZCC_EROFS},
    {ERROR_BAD_UNIT, ZCC_ENXIO},
    {ERROR_NOT_READY, ZCC_EBUSY},
    {ERROR_BAD_COMMAND, ZCC_EIO},
    {ERROR_CRC, ZCC_EIO},
    {ERROR_BAD_LENGTH, ZCC_EIO},
    {ERROR_SEEK, ZCC_EIO},
    {ERROR_NOT_DOS_DISK, ZCC_EIO},
    {ERROR_SECTOR_NOT_FOUND, ZCC_ENXIO},
    {ERROR_OUT_OF_PAPER, ZCC_EBUSY},
    {ERROR_WRITE_FAULT, ZCC_EIO},
    {ERROR_READ_FAULT, ZCC_EIO},
    {ERROR_GEN_FAILURE, ZCC_EIO},
    {ERROR_SHARING_VIOLATION, ZCC_EAGAIN},
    {ERROR_LOCK_VIOLATION, ZCC_EACCES},
    {ERROR_WRONG_DISK, ZCC_ENXIO},
    {35, ZCC_ENFILE},
    {ERROR_SHARING_BUFFER_EXCEEDED, ZCC_ENFILE},
    {ERROR_HANDLE_EOF, ZCC_EINVAL},
    {ERROR_HANDLE_DISK_FULL, ZCC_ENOSPC},
    {ERROR_NOT_SUPPORTED, ZCC_ENOSYS},
    {ERROR_BAD_NETPATH, ZCC_ENOENT},
    {ERROR_NETWORK_ACCESS_DENIED, ZCC_EACCES},
    {ERROR_BAD_NET_NAME, ZCC_ENOENT},
    {ERROR_FILE_EXISTS, ZCC_EEXIST},
    {ERROR_CANNOT_MAKE, ZCC_EACCES},
    {ERROR_FAIL_I24, ZCC_EACCES},
    {ERROR_INVALID_PARAMETER, ZCC_EINVAL},
    {ERROR_NO_PROC_SLOTS, ZCC_EAGAIN},
    {ERROR_DRIVE_LOCKED, ZCC_EACCES},
    {ERROR_BROKEN_PIPE, ZCC_EPIPE},
    {ERROR_BUFFER_OVERFLOW, ZCC_ENAMETOOLONG},
    {ERROR_DISK_FULL, ZCC_ENOSPC},
    {ERROR_INVALID_TARGET_HANDLE, ZCC_EBADF},
    {ERROR_INSUFFICIENT_BUFFER, ZCC_ERANGE},
    {ERROR_INVALID_NAME, ZCC_ENOENT},
    {ERROR_INVALID_HANDLE, ZCC_EINVAL},
    {ERROR_MOD_NOT_FOUND, ZCC_ENOENT},
    {ERROR_PROC_NOT_FOUND, ZCC_ENOENT},
    {ERROR_WAIT_NO_CHILDREN, ZCC_ECHILD},
    {ERROR_CHILD_NOT_COMPLETE, ZCC_ECHILD},
    {ERROR_DIRECT_ACCESS_HANDLE, ZCC_EBADF},
    {ERROR_NEGATIVE_SEEK, ZCC_EINVAL},
    {ERROR_SEEK_ON_DEVICE, ZCC_EACCES},
    {ERROR_DIR_NOT_EMPTY, ZCC_ENOTEMPTY},
    {ERROR_NOT_LOCKED, ZCC_EACCES},
    {ERROR_BAD_PATHNAME, ZCC_ENOENT},
    {ERROR_MAX_THRDS_REACHED, ZCC_EAGAIN},
    {ERROR_LOCK_FAILED, ZCC_EACCES},
    {ERROR_ALREADY_EXISTS, ZCC_EEXIST},
    {ERROR_FILENAME_EXCED_RANGE, ZCC_ENAMETOOLONG},
    {ERROR_NESTING_NOT_ALLOWED, ZCC_EAGAIN},
    {WAIT_TIMEOUT, ZCC_ETIME},
    {ERROR_DIRECTORY, ZCC_ENOTDIR},
    {ERROR_IO_INCOMPLETE, ZCC_EAGAIN},
    {ERROR_IO_PENDING, ZCC_EAGAIN},
    {ERROR_INVALID_FLAGS, ZCC_EINVAL},
    {ERROR_NO_UNICODE_TRANSLATION, ZCC_EINVAL},
    {ERROR_NOT_FOUND, ZCC_ENOENT},
    {ERROR_USER_MAPPED_FILE, ZCC_EACCES},
    {ERROR_PRIVILEGE_NOT_HELD, ZCC_EACCES},
    {ERROR_NOT_ENOUGH_QUOTA, ZCC_ENOMEM},
    {ERROR_ABANDONED_WAIT_0, ZCC_EIO},
    {ERROR_NOT_A_REPARSE_POINT, ZCC_EINVAL},
    {WSAEINTR, ZCC_EINTR},
    {WSAEBADF, ZCC_EBADF},
    {WSAEACCES, ZCC_EACCES},
    {WSAEFAULT, ZCC_EFAULT},
    {WSAEINVAL, ZCC_EINVAL},
    {WSAEMFILE, ZCC_EMFILE},
    {WSAEWOULDBLOCK, ZCC_EWOULDBLOCK},
    {WSAEINPROGRESS, ZCC_EINPROGRESS},
    {WSAEALREADY, ZCC_EALREADY},
    {WSAENOTSOCK, ZCC_ENOTSOCK},
    {WSAEDESTADDRREQ, ZCC_EDESTADDRREQ},
    {WSAEMSGSIZE, ZCC_EMSGSIZE},
    {WSAEPROTOTYPE, ZCC_EPROTOTYPE},
    {WSAENOPROTOOPT, ZCC_ENOPROTOOPT},
    {WSAEPROTONOSUPPORT, ZCC_EPROTONOSUPPORT},
    {WSAEOPNOTSUPP, ZCC_EOPNOTSUPP},
    {WSAEAFNOSUPPORT, ZCC_EAFNOSUPPORT},
    {WSAEADDRINUSE, ZCC_EADDRINUSE},
    {WSAEADDRNOTAVAIL, ZCC_EADDRNOTAVAIL},
    {WSAENETDOWN, ZCC_ENETDOWN},
    {WSAENETUNREACH, ZCC_ENETUNREACH},
    {WSAENETRESET, ZCC_ENETRESET},
    {WSAECONNABORTED, ZCC_ECONNABORTED},
    {WSAECONNRESET, ZCC_ECONNRESET},
    {WSAENOBUFS, ZCC_ENOBUFS},
    {WSAEISCONN, ZCC_EISCONN},
    {WSAENOTCONN, ZCC_ENOTCONN},
    {WSAETIMEDOUT, ZCC_ETIMEDOUT},
    {WSAECONNREFUSED, ZCC_ECONNREFUSED},
    {WSAELOOP, ZCC_ELOOP},
    {WSAENAMETOOLONG, ZCC_ENAMETOOLONG},
    {WSAEHOSTUNREACH, ZCC_EHOSTUNREACH},
    {WSAENOTEMPTY, ZCC_ENOTEMPTY},
    {9, 0},
};
#else // _WIN64
static const struct code_to_errno_map errno_pair[] = {
    {EPERM, ZCC_EPERM},
    {ENOENT, ZCC_ENOENT},
    {ESRCH, ZCC_ESRCH},
    {EINTR, ZCC_EINTR},
    {EIO, ZCC_EIO},
    {ENXIO, ZCC_ENXIO},
    {E2BIG, ZCC_E2BIG},
    {ENOEXEC, ZCC_ENOEXEC},
    {EBADF, ZCC_EBADF},
    {ECHILD, ZCC_ECHILD},
    {EAGAIN, ZCC_EAGAIN},
    {ENOMEM, ZCC_ENOMEM},
    {EACCES, ZCC_EACCES},
    {EFAULT, ZCC_EFAULT},
    {EBUSY, ZCC_EBUSY},
    {EEXIST, ZCC_EEXIST},
    {EXDEV, ZCC_EXDEV},
    {ENODEV, ZCC_ENODEV},
    {ENOTDIR, ZCC_ENOTDIR},
    {EISDIR, ZCC_EISDIR},
    {EINVAL, ZCC_EINVAL},
    {ENFILE, ZCC_ENFILE},
    {EMFILE, ZCC_EMFILE},
    {ENOTTY, ZCC_ENOTTY},
    {EFBIG, ZCC_EFBIG},
    {ENOSPC, ZCC_ENOSPC},
    {ESPIPE, ZCC_ESPIPE},
    {EROFS, ZCC_EROFS},
    {EMLINK, ZCC_EMLINK},
    {EPIPE, ZCC_EPIPE},
    {EDOM, ZCC_EDOM},
    {ERANGE, ZCC_ERANGE},
    {EDEADLK, ZCC_EDEADLK},
    {ENAMETOOLONG, ZCC_ENAMETOOLONG},
    {ENOLCK, ZCC_ENOLCK},
    {ENOSYS, ZCC_ENOSYS},
    {ENOTEMPTY, ZCC_ENOTEMPTY},
    {EILSEQ, ZCC_EILSEQ},
#ifdef EDEADLOCK
    {EDEADLOCK, ZCC_EDEADLOCK},
#endif // EDEADLOCK
    {EADDRINUSE, ZCC_EADDRINUSE},
    {EADDRNOTAVAIL, ZCC_EADDRNOTAVAIL},
    {EAFNOSUPPORT, ZCC_EAFNOSUPPORT},
    {EALREADY, ZCC_EALREADY},
    {EBADMSG, ZCC_EBADMSG},
    {ECANCELED, ZCC_ECANCELED},
    {ECONNABORTED, ZCC_ECONNABORTED},
    {ECONNREFUSED, ZCC_ECONNREFUSED},
    {ECONNRESET, ZCC_ECONNRESET},
    {EDESTADDRREQ, ZCC_EDESTADDRREQ},
    {EHOSTUNREACH, ZCC_EHOSTUNREACH},
    {EIDRM, ZCC_EIDRM},
    {EINPROGRESS, ZCC_EINPROGRESS},
    {EISCONN, ZCC_EISCONN},
    {ELOOP, ZCC_ELOOP},
    {EMSGSIZE, ZCC_EMSGSIZE},
    {ENETDOWN, ZCC_ENETDOWN},
    {ENETRESET, ZCC_ENETRESET},
    {ENETUNREACH, ZCC_ENETUNREACH},
    {ENOBUFS, ZCC_ENOBUFS},
    {ENODATA, ZCC_ENODATA},
    {ENOLINK, ZCC_ENOLINK},
    {ENOMSG, ZCC_ENOMSG},
    {ENOPROTOOPT, ZCC_ENOPROTOOPT},
    {ENOSR, ZCC_ENOSR},
    {ENOSTR, ZCC_ENOSTR},
    {ENOTCONN, ZCC_ENOTCONN},
    {ENOTRECOVERABLE, ZCC_ENOTRECOVERABLE},
    {ENOTSOCK, ZCC_ENOTSOCK},
    {ENOTSUP, ZCC_ENOTSUP},
    {EOPNOTSUPP, ZCC_EOPNOTSUPP},
    {EOVERFLOW, ZCC_EOVERFLOW},
    {EOWNERDEAD, ZCC_EOWNERDEAD},
    {EPROTO, ZCC_EPROTO},
    {EPROTONOSUPPORT, ZCC_EPROTONOSUPPORT},
    {EPROTOTYPE, ZCC_EPROTOTYPE},
    {ETIME, ZCC_ETIME},
    {ETIMEDOUT, ZCC_ETIMEDOUT},
    {ETXTBSY, ZCC_ETXTBSY},
    {EWOULDBLOCK, ZCC_EWOULDBLOCK},
    {0, 0},
};
#endif // _WIN64

static void _register_errno_map()
{
    for (uint64_t i = 0; i < sizeof(errno_pair) / sizeof(struct code_to_errno_map); ++i)
    {
        errno_map_sys_to_zcc[errno_pair[i].from] = errno_pair[i].to;
        errno_map_zcc_to_sys[errno_pair[i].to] = errno_pair[i].from;
    }
}

zcc_global_init(_register_errno_map());

int get_errno(int code)
{
    if (code == 0)
    {
#ifdef _WIN64
        code = GetLastError();
#else  // _WIN64
        code = errno;
#endif // _WIN64
    }
    if (code == 0)
    {
        return 0;
    }
    auto it = errno_map_sys_to_zcc.find(code);
    if (it == errno_map_sys_to_zcc.end())
    {
        return ZCC_EINVAL;
    }
    return it->second;
}

void set_errno(int code)
{
    int nc = code;
    auto it = errno_map_zcc_to_sys.find(code);
    if (it != errno_map_zcc_to_sys.end())
    {
        nc = it->second;
    }
#ifdef _WIN64
    if (nc > 1000)
    {
        WSASetLastError(nc);
    }
    SetLastError(nc);
#else  // _WIN64
    errno = nc;
#endif // _WIN64
}

zcc_namespace_end;
